﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using System.IO;
using System.Threading;
using System.Collections;
using System.Diagnostics;

namespace NewCROStarter
{
    public partial class DXoverlay : Form
    {
        public DXoverlay()
        {
            InitializeComponent();
            InitializeGraphics();
        }

        Device device = null;
        Microsoft.DirectX.Direct3D.Texture texture;
        Microsoft.DirectX.Direct3D.Sprite sprite;
        Rectangle TextureSize;

        private void InitializeGraphics()
        {
            try
            {
                SetWindow.SetWindowLongInt(base.Handle,SetWindow.WindowLongFlags.GWL_EXSTYLE,(SetWindow.GetWindowLongInt(base.Handle,SetWindow.WindowLongFlags.GWL_EXSTYLE)^ 0x80000) ^ 0x20);
                SetWindow.SetLayeredWindowAttributes(base.Handle, 0, 0xff, 2);
                PresentParameters presentParams = new PresentParameters();
                presentParams.Windowed = true;
                presentParams.SwapEffect = SwapEffect.Discard;
                presentParams.EnableAutoDepthStencil = true;
                presentParams.AutoDepthStencilFormat = DepthFormat.D16;

                device = new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams);
                //load texture
                //在初始化时load一个透明文件
                texture = TextureLoader.FromFile(device, Application.StartupPath + @"\NewCROStarter\Maps\1x1.png");
                using (Surface s = texture.GetSurfaceLevel(0))
                {
                    SurfaceDescription sd = s.Description;
                    TextureSize = new Rectangle(0, 0, sd.Width, sd.Height);
                }
                sprite = new Sprite(device);
            }
            catch (DirectXException)
            {            }
        }

        //绘制图片
        public void Render()
        {
            if (device == null) return;
            device.Clear(ClearFlags.Target, Color.FromArgb(0, 0, 0, 0), 1.0f, 0);
            device.BeginScene();
            sprite.Begin(SpriteFlags.AlphaBlend);
            sprite.Draw(texture, TextureSize, new Vector3(0, 0, 0), new Vector3(0, 0, 0), Color.White);
            sprite.End();
            device.EndScene();
            device.Present();
        }

        //输入：地图名，x，y，附加text数组，显示模式，缩放，proc/caption用于确定目标程序的窗口面积/范围
        //输入坐标（含附加text）均为ro地图原始坐标，ro地图左下角是0，0！
        //todo:本版本未实现文本的绘制
        //todo:这里既可以传入proc，也可以传入caption，现在默认采用proc
        public void RenderROMap(string Mapname, Process proc, int Char_x, int Char_y, List<ROMapText> texts, DisplayType displaytype, float zoom,string caption)
        {
            StreamWriter sw = new StreamWriter("debug.txt", true);
            sw.WriteLine("in "+ Mapname + " " + proc.ToString() + " " + Char_x.ToString() + "x" + Char_y.ToString());
            //取得材质大小
            SurfaceDescription sd;
            sd = ((Texture)ROMaps[Mapname]).GetLevelDescription(0);
            //初始化绘图位置
            int display_x = 0, display_y = 0;
            //初始化角色坐标
            int char_x = Char_x, char_y = Char_y;
            //角色坐标变换（传入的ro坐标左下角为原点，directx屏幕坐标左上角为原点，坐标计算c#X=roX,c#Y=MaxY-roY）
            //todo:原来写的是char_y = sd.Width - Char_y,按公式应该是char_y = sd.Height - Char_y，需要一个长宽不等的小地图做测试
            char_y = sd.Height - Char_y;
            //文本坐标变换
            //foreach (ROMapText text2 in texts)
            //{
            //    text2.x_pos_dx = text2.x_pos_ro;
            //    text2.y_pos_dx = sd.Height - text2.y_pos_ro;
            //}
            //小地图角色光标的中心点
            int char_center_x=0, char_center_y=0;
            //根据指定的小地图显示类型进行ro坐标系到screen坐标系的变换
            switch (displaytype)
            {
                //在ro窗体中间显示地图
                //把传入的坐标放到ro窗体中间即可
                case DisplayType.App_CharacterCenter:
                    #region
                    //取得目标窗口的可绘图区域
                    SetWindow.RECT clientRect2 = SetWindow.GetClientRect(proc);
                    Point r_point2 = new Point(0, 0);
                    //传入client中心点坐标
                    r_point2.X = Convert.ToInt32(Math.Truncate((float)clientRect2.right * 0.5));
                    r_point2.Y = Convert.ToInt32(Math.Truncate((float)clientRect2.bottom * 0.5));
                    //传出client中心点屏幕坐标
                    SetWindow.ClientToScreen(proc, ref r_point2);
                    //取得绘图起点，=中心点-中心点-(坐标系变换后的)传入坐标
                    display_x = r_point2.X - char_x;
                    display_y = r_point2.Y - char_y;
                    //取得缩放后的绘图起点(=绘图起点/缩放倍率)
                    display_x = Convert.ToInt32(Math.Truncate((float)display_x / zoom));
                    display_y = Convert.ToInt32(Math.Truncate((float)display_y / zoom));
                    break;
                    #endregion
                //在屏幕右上角显示地图
                case DisplayType.Window_UpperRightCorner:
                    #region
                    //取得屏幕大小
                    Rectangle r = Screen.GetWorkingArea(this);
                    //取得绘图起点
                    display_x = r.Width - Convert.ToInt32(Math.Truncate((float)sd.Width * zoom));
                    //取得char_x，char_y屏幕坐标（ro坐标系和小地图分辨率具有1：1对应关系）
                    //dixplay_x地图左上角，char_x*zoom为小地图内部坐标系缩放后，加上display_x即可得到屏幕坐标
                    char_x = display_x + Convert.ToInt32(Math.Truncate((float)char_x * zoom));
                    //取得缩放后的绘图起点(=绘图起点/缩放倍率)
                    display_x = Convert.ToInt32(Math.Truncate((float)display_x / zoom));
                    char_x = Convert.ToInt32(Math.Truncate((float)char_x / zoom));
                    char_y = display_y + Convert.ToInt32(Math.Truncate((float)char_y * zoom));
                    char_y = Convert.ToInt32(Math.Truncate((float)char_y / zoom));
                    //取小地图角色光标的中心点
                    sd = ((Texture)ROMaps["10x10_dot"]).GetLevelDescription(0);
                    char_center_x = Convert.ToInt32(Math.Truncate((float)sd.Width / 2.0f));
                    char_center_y = Convert.ToInt32(Math.Truncate((float)sd.Height / 2.0f));
                    break;
                    #endregion
                //在ro窗体右上角显示地图
                case DisplayType.App_UpperRightCorner:
                    #region
                    //取得目标窗口的可绘图区域
                    SetWindow.RECT clientRect = SetWindow.GetClientRect(proc);
                    Point r_point = new Point(0, 0);
                    //传入client右上角坐标
                    r_point.X = clientRect.right;
                    r_point.Y = 0;
                    //传出client右上角屏幕坐标
                    SetWindow.ClientToScreen(proc, ref r_point);
                    //取得绘图起点，窗体右上角的坐标=ClientToScreen(x=目标窗体rect.right，y=0)
                    display_x = r_point.X - Convert.ToInt32(Math.Truncate((float)sd.Width * zoom));
                    display_y = r_point.Y;
                    //取得char_x，char_y屏幕坐标（ro坐标系和小地图分辨率具有1：1对应关系）
                    //dixplay_x地图左上角，char_x*zoom为小地图内部坐标系缩放后，加上display_x即可得到屏幕坐标
                    char_x = display_x + Convert.ToInt32(Math.Truncate((float)char_x * zoom));
                    char_y = display_y + Convert.ToInt32(Math.Truncate((float)char_y * zoom));
                    //取得缩放后的绘图起点(=绘图起点/缩放倍率)
                    display_x = Convert.ToInt32(Math.Truncate((float)display_x / zoom));
                    display_y = Convert.ToInt32(Math.Truncate((float)display_y / zoom));
                    char_x = Convert.ToInt32(Math.Truncate((float)char_x / zoom));
                    char_y = Convert.ToInt32(Math.Truncate((float)char_y / zoom));
                    //取小地图角色光标的中心点
                    sd = ((Texture)ROMaps["10x10_dot"]).GetLevelDescription(0);
                    char_center_x = Convert.ToInt32(Math.Truncate((float)sd.Width / 2.0f));
                    char_center_y = Convert.ToInt32(Math.Truncate((float)sd.Height / 2.0f));
                    break;
                    #endregion
            }
            if (device == null) return;
            device.Clear(ClearFlags.Target, Color.FromArgb(0, 0, 0, 0), 1.0f, 0);
            device.BeginScene();
            //设置缩放倍率,默认应为1.0f
            Matrix m = Microsoft.DirectX.Matrix.Scaling(zoom, zoom, 1.0f);
            sprite.Transform = m;
            sprite.Begin(SpriteFlags.AlphaBlend);
            //首先画地图
            sprite.Draw((Texture)ROMaps[Mapname], System.Drawing.Rectangle.Empty, new Vector3(0, 0, 0), new Vector3(display_x, display_y, 0), Color.White);
            //如果DisplayType=App_UpperRightCorner或Window_UpperRightCorner，画光标
            if (displaytype == DisplayType.App_UpperRightCorner || displaytype == DisplayType.Window_UpperRightCorner)
            {
                sprite.Draw((Texture)ROMaps["10x10_dot"], System.Drawing.Rectangle.Empty, new Vector3(char_center_x, char_center_y, 0), new Vector3(char_x, char_y, 0), Color.White);
                sw.WriteLine("out char center" + char_center_x.ToString() + "x" + char_center_y.ToString() + " pos" + char_x.ToString() + "X" + char_y.ToString());
            }
            //draw text
            //foreach (ROMapText text2 in texts)
            //{
            //    //System.Drawing.Font fontstyle = new System.Drawing.Font("宋体", 18f);
            //    //font = new Microsoft.DirectX.Direct3D.Font(device, fontstyle);
            //    font = new Microsoft.DirectX.Direct3D.Font(device, text2.font_style);
            //    font.DrawText(sprite, text2.text, new Point(text2.x_pos_dx, text2.y_pos_dx), text2.font_color);
            //}
            sprite.End();
            device.EndScene();
            device.Present();
            sw.Close();
        }

        Hashtable ROMaps;
        //从指定目录缓存所有png到texture，并存入hashtable，key=地图文件名（不带扩展名）
        //注意该方法可能会将大量图片载入显存
        //材质需要power of 2，即TextureLoader.FromFile会自动把材质长宽放大为最接近2^n的大小
        //http://stackoverflow.com/questions/4626490/sprite-draw-draws-my-textures-too-small
        //http://www.katsbits.com/tutorials/textures/make-better-textures-correct-size-and-power-of-two.php
        //todo:看看所有的小地图bmp，是否都能power of 2
        public void InitRenderROMap(string path)
        {
            ROMaps = new Hashtable();
            foreach (string file in Directory.GetFiles(path, "*.png", SearchOption.TopDirectoryOnly))
            {
                //取得文件名（不含扩展名）
                string tmpfile = file.Remove(0, file.LastIndexOf('\\')+1);
                tmpfile = tmpfile.Replace(".png", "");
                Texture t = TextureLoader.FromFile(device, file);
                ROMaps.Add(tmpfile, t);
            }
        }

        SetWindow.Margins marg;
        private void DXoverlay_Paint(object sender, PaintEventArgs e)
        {
            this.marg.Left = 0;
            this.marg.Top = 0;
            this.marg.Right = base.Width;
            this.marg.Bottom = base.Height;
            //使用最大化运行窗体，DwmExtendFrameIntoClientArea可以使窗体黑色部分变透明，再在上面画上directx图形，即可实现穿透覆盖，该api为vista以上可用
            SetWindow.DwmExtendFrameIntoClientArea(base.Handle, ref this.marg);
            Render();
        }

        private void DXoverlay_FormClosing(object sender, FormClosingEventArgs e)
        {
            sprite.Dispose();
            device.Dispose();
        }

        private void DXoverlay_Load(object sender, EventArgs e)
        {

        }
    }
}
